//! Alice 引擎目前定位是一个玩具游戏引擎、实现参考了 Hazel 引擎，在引擎核心之外添加分层机制，通过分层机制来实现
//! 对引擎添加额外功能。 比如编辑器、运行时等宿主环境。
//! 目前的引擎实现不会提供主循环，需要在宿主环境来提供事件循环系统，可以通过 Application::create_main_loop() 来创建一个事件循环
//! 





use std::{time::Instant};


use winit::{
    event::{Event, KeyboardInput, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
};

use crate::{
    context::AliceContext,
    math::{Vector2f, Vector2u},
    event::{
       KeyPressedEvent, KeyReleasedEvent,
            MouseButtonPressedEvent, MouseButtonReleasedEvent, MouseMoveEvent, MouseScrollEvent,
   
    },
    function::{
        input::{convert_element_state, convert_mouse_button, convert_virtual_key_code},
        layer::{layer::Layer, stack::LayerStack}, scene::scene::Scene
    }, input::INPUT,
};

#[derive(Debug, Default)]
pub struct Time(f64);


/// 
/// Example:
///
/// ```rust
/// # use alice2d::Application;
/// let game_loop   = Application::create_main_loop();
/// let mut app     = Application::new(&game_loop);
/// let editor      = Box::new(EditorLayer::new(app.ctx().clone()));
/// app.push_layer(editor);
/// 
/// ```
/// 引擎启动需要提供一个事件循环，引擎
/// 
pub struct App {
    /// 引擎内部管理各个模块协调工作的上下文、可以被轻易 Clone
    ctx:                AliceContext,
    /// 引擎提供的分层系统、内部是事件与渲染会通过分层系统来进行传递
    layers:             LayerStack,
    /// 上一帧执行的时间  dt = now - last_frame_time
    last_frame_time:    Instant,
    /// 引擎启动的开始时间
    pub start_time:         Instant,
    active_scene:       Option<Scene>,
    /// 引擎版本号
    version:            &'static str,
}

impl App {
    pub fn new(main_loop: &EventLoop<()>) -> App {
        let ctx = AliceContext::new(main_loop);
 
        App {
           
            ctx,
            layers: LayerStack::default(),
            last_frame_time: Instant::now(),
            start_time: Instant::now(),
            version: "0.0.1",
            active_scene:None
        }
    }

    pub fn push_layer(&mut self, layer: Box<dyn Layer>) {
        self.layers.push_layer(layer)
    }

    pub fn push_over_layer(&mut self, layer: Box<dyn Layer>) {
        self.layers.push_over_layer(layer)
    }

    pub fn pop_layer(&mut self, layer: &Box<dyn Layer>) {
        self.layers.pop_layer(layer)
    }

    pub fn pop_over_layer(&mut self, layer: &Box<dyn Layer>) {
        self.layers.pop_over_layer(layer)
    }

    pub fn get_layer<T:Layer>(&mut self, layer_name:&str) -> &mut T {
        self.layers.iter_mut().find(|l|l.get_name() == layer_name).unwrap().as_any_mut().downcast_mut::<T>().unwrap()
    }

    pub fn ctx(&self) -> &AliceContext {
        &self.ctx
    }
    pub fn ctx_mut(&mut self) -> &mut AliceContext {
        &mut self.ctx
    }

    fn calc_per_frame_delta(&mut self) -> f64 {
        let now = std::time::Instant::now();
        let delta = now.duration_since(self.last_frame_time).as_secs_f64();
        self.last_frame_time = now.clone();
        delta
    }

    fn tick_per_frame(&mut self) {
        let dt = self.calc_per_frame_delta();

        self.begin_per_frame();

        self.logic_tick_per_frame(dt);

        self.tick_gui_render();

        self.render_tick_per_frame();

        self.end_per_frame();
    }

    fn logic_tick_per_frame(&mut self, dt: f64) {
        for layer in self.layers.iter_mut() {
            layer.update(dt)
        }
    }

    fn tick_gui_render(&mut self) {
        let ctx = &mut self.ctx.borrow_mut();
        let per_ui_frame = {
 
            let ui = &mut ctx.ui;
            ui.begin_frame(self.start_time);
            for layer in self.layers.iter_mut() {
                layer.ui_render(&mut ui.ctx())
            }

            ui.end_frame()
        };

        ctx.render.update_ui_output(per_ui_frame);
    }

    fn begin_per_frame(&mut self) {
        self.ctx.begin_frame();
        for layer in self.layers.iter_mut() {
            layer.begin_frame()
        }
    }

    fn render_tick_per_frame(&mut self) {
        self.ctx.borrow_mut().tick()
    }


    fn end_per_frame(&mut self){
        // fn end_frame(&mut self);
        for layer in self.layers.iter_mut() {
            layer.end_frame()
        }
    }

    fn handle_event(&mut self, event: &mut Box<dyn crate::event::Event>) {
        for layer in self.layers.iter_mut_rev() {
            layer.event(event);
            if event.get_handle() {
                break;
            }
        }
    }

    /// swap logic data to render data and update render data
    // fn swap_logic_data_to_render_data(&mut self) {
    //     // render_system.swap_ctx.sync_cache_to_logic_data();
    // }

    pub fn get_version(&self) -> &'static str {
        self.version
    }

    pub fn set_active_scene(&mut self,scene:Scene){
        self.active_scene = Some(scene);
    }

    pub fn get_active_scene(&mut self) -> &mut Option<Scene>{
        &mut self.active_scene
    }


 

    pub fn run<T>(&mut self,event:Event<'_, T>,control_flow: &mut ControlFlow) {
        self.ctx.borrow_mut().ui.handle_ui_event(&event);
        match event {
            Event::WindowEvent {
                ref event,
                window_id: _,
            } => match event {
                WindowEvent::CloseRequested => {
                    *control_flow = ControlFlow::Exit;
                }
                WindowEvent::Resized(size) => {
                    self.ctx.resize(Vector2u::new(size.width, size.height));
                }
                // WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size } => {

                // }
                WindowEvent::KeyboardInput {
                    input:
                        KeyboardInput {
                            state,
                            virtual_keycode,
                            ..
                        },
                    ..
                } => {
                    use crate::event::Event;
                    if let Some(virtual_keycode) = virtual_keycode {
                        let code = convert_virtual_key_code(virtual_keycode);

                        let state = convert_element_state(*state);

                        let mut event: Box<dyn Event> = match state {
                            crate::function::input::AliceInputState::Pressed => {
                                Box::new(KeyPressedEvent::new(code))
                            }
                            crate::function::input::AliceInputState::Released => {
                                Box::new(KeyReleasedEvent::new(code))
                            }
                            crate::function::input::AliceInputState::Unknown => {
                                Box::new(KeyPressedEvent::new(code))
                            }
                        };

                        self.handle_event(&mut event)
                    }
                    
                    unsafe {
                        if let Some(input) =INPUT.get_mut() {
                            input.on_key_input(*state, *virtual_keycode)
                        }
                    }
                 
                }

                WindowEvent::MouseInput { state, button, .. } => {
                    use crate::event::Event;

                    unsafe {
                        if let Some(input) =INPUT.get_mut() {
                            input.on_mouse_button_input(*state, *button)
                        }
                    }
              
                    let button = convert_mouse_button(*button);

                    let state = convert_element_state(*state);

                    let mut event: Box<dyn Event> = match state {
                        crate::function::input::AliceInputState::Pressed => {
                            Box::new(MouseButtonPressedEvent::new(button))
                        }
                        crate::function::input::AliceInputState::Released => {
                            Box::new(MouseButtonReleasedEvent::new(button))
                        }
                        crate::function::input::AliceInputState::Unknown => {
                            Box::new(MouseButtonPressedEvent::new(button))
                        }
                    };

                    self.handle_event(&mut event)
                }
                WindowEvent::MouseWheel { delta, .. } => {
                    use crate::event::Event;

                    let mut delta = match delta {
                        winit::event::MouseScrollDelta::LineDelta(x, y) => {
                            let line_height = 8.0; // TODO as in egui_glium
                            Vector2f::new(*x, *y) * line_height
                        }
                        winit::event::MouseScrollDelta::PixelDelta(delta) => {
                            Vector2f::new(delta.x as f32, delta.y as f32)
                        }
                    };
                    if cfg!(target_os = "macos") {
                        // See https://github.com/rust-windowing/winit/issues/1695 for more info.
                        delta.x *= -1.0;
                    }

                    let mut event: Box<dyn Event> = Box::new(MouseScrollEvent::new(delta));
                    self.handle_event(&mut event)
                }
                WindowEvent::CursorMoved { position, .. } => {
                    use crate::event::Event;
               
                    unsafe {
                        if let Some(input) =INPUT.get_mut() {
                            input.on_cursor_moved(*position);
                        }
                    }
                    let pos = Vector2f::new(position.x as f32, position.y as f32);

                    let mut event: Box<dyn Event> = Box::new(MouseMoveEvent::new(pos));
                    self.handle_event(&mut event)
                }
                WindowEvent::CursorLeft { .. } => {}
                // WindowEvent::TouchpadPressure {device_id, pressure, stage, ..  } => {} // TODO
                WindowEvent::Touch(_touch) => {}
                WindowEvent::ReceivedCharacter(_ch) => {
                    // On Mac we get here when the user presses Cmd-C (copy), ctrl-W, etc.
                    // We need to ignore these characters that are side-effects of commands.
                    // let input
                    // let is_mac_cmd = cfg!(target_os = "macos")
                    //     && (self.ctx.borrow_mut().ui.input.modifiers.ctrl
                    //         || self.ctx.borrow_mut().ui.input.modifiers.mac_cmd);

                    // if is_printable_char(*ch) && !is_mac_cmd {
                    //     self.ctx.borrow_mut().ui.input
                    //         .events
                    //         .push(egui::Event::Text(ch.to_string()));
                    //     self.ctx.borrow_mut().ui.ctx.wants_keyboard_input()
                    // } else {
                    //     false
                    // }
                }

                WindowEvent::Focused(_has_focus) => {}
                WindowEvent::HoveredFile(_path) => {}
                WindowEvent::HoveredFileCancelled => {}
                WindowEvent::DroppedFile(_path) => {}
                WindowEvent::ModifiersChanged(_state) => {}
                _ => {
                    // dbg!(event);
                }
            },
            Event::RedrawRequested(_window_id) => {
                //  input_ctx = std::mem::take(&mut input_ctx).begin_time(input.take());

                self.tick_per_frame();

                //  gui.begin_frame();

                //  gui.tick_per_frame();

                //  let output = gui.end_frame();

                //  render_pipeline.draw(&output);
            }
            Event::MainEventsCleared => {
                // 除非手动请求，否则 RedrawRequested 只会触发一次
                //  window.get_window().request_redraw()
                // if count < 1 {
                  
                //     count += 1;
                // }
                self.ctx.borrow_mut().window.request_draw();
              
            }
            _ => {}
        }
   
    }
}

impl App {
    pub fn create_main_loop() -> EventLoop<()> {
        EventLoop::new()
    }
}
